package edu.hda.pt.utils.connections.netty.codec;

import static org.jboss.netty.channel.Channels.write;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipelineCoverage;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;

import edu.hda.pt.client.Config;
import edu.hda.pt.utils.Stream;
import edu.hda.pt.utils.TestObject;
import edu.hda.pt.utils.Stream.StreamState;

/**
 * This class encodes a message before it is sent to either the client or the
 * server
 * 
 * @author Marc Hammerton
 * 
 */
@ChannelPipelineCoverage("all")
public class MessageEncoder extends SimpleChannelHandler {

	/**
	 * This method encodes a message. by adding a header to the value and
	 * putting the whole message into a buffer and writing it to the next
	 * handler.
	 * 
	 * @see org.jboss.netty.channel.SimpleChannelHandler#writeRequested(ChannelHandlerContext,
	 *      MessageEvent)
	 */
	@Override
	public void writeRequested(ChannelHandlerContext chx, MessageEvent e)
			throws Exception {

		ChannelBuffer buf = ChannelBuffers.dynamicBuffer();

		if (e.getMessage() instanceof Boolean) {
			buf.writeShort(Config.HANDLEBOOL);
			buf.writeByte(((Boolean) e.getMessage()) == true ? (byte) 1
					: (byte) 0);
		} else if (e.getMessage() instanceof byte[]) {
			byte[] data = (byte[]) e.getMessage();

			buf.writeShort(Config.HANDLEBYTES);
			buf.writeInt(data.length);
			buf.writeBytes(data);
		} else if (e.getMessage() instanceof Integer) {
			buf.writeShort(Config.HANDLEINTEGER);
			buf.writeInt((Integer) e.getMessage());
		} else if (e.getMessage() instanceof Long) {
			buf.writeShort(Config.HANDLELONG);
			buf.writeLong((Long) e.getMessage());
		} else if (e.getMessage() instanceof TestObject) {
			TestObject object = (TestObject) e.getMessage();

			// Convert Object to ByteArray
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(bos);
			oos.writeObject(object);
			oos.flush();
			oos.close();
			bos.close();
			byte[] data = bos.toByteArray();

			buf.writeShort(Config.HANDLEOBJECT);
			buf.writeInt(data.length);
			buf.writeBytes(data);
		} else if (e.getMessage() instanceof String) {
			byte[] data = ((String) e.getMessage()).getBytes();

			buf.writeShort(Config.HANDLESTRING);
			buf.writeInt(data.length);
			buf.writeBytes(data);
		} else if (e.getMessage() instanceof Stream) {
			Stream stream = (Stream) e.getMessage();

			if (stream.getState() == StreamState.START) {
				buf.writeShort(Config.HANDLESTREAM_START);
				buf.writeInt(stream.getFs());
			} else if (stream.getState() == StreamState.RUNNING) {
				buf.writeShort(Config.HANDLESTREAM_STARTED);
			} else if (stream.getState() == StreamState.FINISH) {
				buf.writeShort(Config.HANDLESTREAM_STOP);
			}
		}

		// Send the constructed message.
		write(chx, e.getChannel(), e.getFuture(), buf, e.getRemoteAddress());
	}
}
